package com.laowang.framework.annotation.impl;


import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.laowang.common.exception.CustomException;
import com.laowang.framework.annotation.AccessLimit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * 限流切面
 * Created by liugh on 2018/10/12.
 */
@Slf4j
@Component
@Aspect
public class AccessLimitAspect {



    //添加速率.保证是单例的
    private static RateLimiter rateLimiter = RateLimiter.create(1000);
     //使用url做为key,存放令牌桶 防止每次重新创建令牌桶
    private static  Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

    @Before("@annotation(accessLimit)")
    public void execute(JoinPoint joinPoint, AccessLimit accessLimit) throws Throwable{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodname = signature.getMethod().getName();
        System.out.println("方法名:"+methodname);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 或者url(存在map集合的key)
        String url = request.getRequestURI();
        if (!limitMap.containsKey(url)) {
            // 创建令牌桶
            rateLimiter = RateLimiter.create(accessLimit.perSecond());
            limitMap.put(url, rateLimiter);
            log.info("<<=================  请求{},创建令牌桶,容量{} 成功!!!",url,accessLimit.perSecond());
        }
        rateLimiter = limitMap.get(url);
        if (!rateLimiter.tryAcquire(accessLimit.timeOut(), accessLimit.timeOutUnit())) {//获取令牌
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            log.info("Error ---时间:{},获取令牌失败.", sdf.format(new Date()));
            throw new CustomException("服务器繁忙，请稍后再试!");
        }

    }
}
